package com.example.globject;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

import com.example.glutil.TextureHelper;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import static com.example.renderer.ScreenRenderer.checkGlError;

/**
 * Created by Lucas on 12/06/13.
 */
public class GLSquare {

    /**
     * the size of byte in a vertex
     */
    private static final int FLOAT_SIZE_BYTES = 4;
    /**
     * The number of coordinates
     */
    private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    /**
     * The offset of the object coordinate
     */
    private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    /**
     * The offset of the texture of the object
     */
    private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    /**
     * The Zindex initilized at 0 because we are dealing with a 2D scene
     */
    private final float ZINDEX = 0f;
    /**
     * The angle of the object
     */
    private float _angle;
    /**
     * the vertices that transports the data
     */
    private float[] _squareVerticeData;
    /**
     * The vertice that transports the data
     */
    private FloatBuffer _squareVertice;
    /**
     * The openGL model-View-Projection matric
     */
    private float[] mMVPMatrix = new float[16];
    /**
     * The projection Matrix
     */
    private float[] mProjMatrix = new float[16];
    /**
     * The model Matrix
     */
    private float[] mMMatrix = new float[16];
    /**
     * The View Matrix
     */
    private float[] mVMatrix = new float[16];
    /**
     * The rotation Matrix
     */
    private float[] rotation = new float[16];
    /**
     * the id of the resource texture
     */
    private int _resourceTextureId;
    /**
     * the id of the texture
     */
    private int mTextureID;
    /**
     * the id of the model-view-projection matrix
     */
    private int muMVPMatrixHandle;
    /**
     * The id of the position
     */
    private int maPositionHandle;
    /**
     * the id of the texture
     */
    private int maTextureHandle;
    /**
     * The context of the application
     */
    private Context _context;
    /**
     * The debug TAG
     */
    private static String TAG = "GLSquare";
    /**
     * the coordinate x,y of the origin point
     */
    private float _xCenter, _yCenter;
    /**
     * the width and height of the object
     */
    private float _midWidth, _midHeight;

    /**
     * The constructor of the glObject
     * @param context the context of the application
     * @param xCenter the coordinate x of the origin point
     * @param yCenter the coordinate y of the origin point
     * @param midHeight the height of the object
     * @param midWidth the width of the object
     * @param resourceTextureId the resource id of the texture
     */
    public GLSquare(Context context, float xCenter, float yCenter, float midHeight, float midWidth, int resourceTextureId) {
        _xCenter = xCenter;
        _yCenter = yCenter;
        _midWidth = midWidth;
        _midHeight = midHeight;

        _squareVerticeData = new float[]{
                // X, Y, Zs, U, V
                xCenter-midHeight, yCenter+midWidth, ZINDEX, 1.0f, 1.0f, //top-left
                xCenter+midHeight, yCenter+midWidth, ZINDEX, 1.0f, 0.0f, //top-right
                xCenter-midHeight, yCenter-midWidth, ZINDEX, 0.0f, 1.0f, //bottom-left
                xCenter+midHeight, yCenter-midWidth, ZINDEX, 0.0f, 0.0f}; //bottom-right

        _angle = 0f;
        _context = context;
        _resourceTextureId = resourceTextureId;
        _squareVertice = ByteBuffer.allocateDirect(_squareVerticeData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        _squareVertice.put(_squareVerticeData).position(0);
    }

    /**
     * set the handler of the globject with the matching matrix
     * @param positionHandle
     * @param textureHandle
     * @param MVPMatrixHandle
     * @param vMatrix
     * @param projMatrix
     * @param text
     */
    public void setGLHandler(int positionHandle, int textureHandle, int MVPMatrixHandle,
                             float vMatrix[], float projMatrix[], String text){
        Log.i(TAG, "setGLHandler");
        maPositionHandle = positionHandle;
        muMVPMatrixHandle = MVPMatrixHandle;
        maTextureHandle = textureHandle;
        mVMatrix = vMatrix;
        mProjMatrix = projMatrix;
        mTextureID = TextureHelper.loadTexture(_context, _resourceTextureId, text);

    }

    /**
     * rotate the object according to the angle
     * @param angle the angle of rotation
     */
    public void rotate(float angle){
        Log.i(TAG, "rotation of " + angle);
        _angle = ((_angle+angle)%360)+1;
    }

    /**
     * Move the gl object to the position of the parameters
     * @param toX the coordinate x of the origin point
     * @param toY the coordinate y of the origin point
     */
    public void moveTo(float toX, float toY){
        //Log.d(TAG, "Move to ("+toX+","+toY+")");
        _xCenter = toX;
        _yCenter = toY;

        _squareVerticeData = new float[]{
                // X, Y, Zi, U, V
                _xCenter-_midHeight, _yCenter+_midWidth, ZINDEX, 1.0f, 1.0f, //top-left
                _xCenter+_midHeight, _yCenter+_midWidth, ZINDEX, 1.0f, 0.0f, //top-right
                _xCenter-_midHeight, _yCenter-_midWidth, ZINDEX, 0.0f, 1.0f, //bottom-left
                _xCenter+_midHeight, _yCenter-_midWidth, ZINDEX, 0.0f, 0.0f}; //bottom-right
        _squareVertice = ByteBuffer.allocateDirect(_squareVerticeData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        _squareVertice.put(_squareVerticeData).position(0);
    }

    /**
     * Move the gl object of the the position of the parameters
     * @param xDirection the x offset coordinate
     * @param yDirection the y offset coordinate
     */
    public void moveOf(float xDirection, float yDirection) {
        //Log.d(TAG, "Move of ("+xDirection+","+yDirection+")");
        _xCenter += xDirection;
        _yCenter += yDirection;

        _squareVerticeData = new float[]{
                // X, Y, Zi, U, V
                _xCenter-_midHeight, _yCenter+_midWidth, ZINDEX, 1.0f, 1.0f, //top-left
                _xCenter+_midHeight, _yCenter+_midWidth, ZINDEX, 1.0f, 0.0f, //top-right
                _xCenter-_midHeight, _yCenter-_midWidth, ZINDEX, 0.0f, 1.0f, //bottom-left
                _xCenter+_midHeight, _yCenter-_midWidth, ZINDEX, 0.0f, 0.0f}; //bottom-right
        _squareVertice = ByteBuffer.allocateDirect(_squareVerticeData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        _squareVertice.put(_squareVerticeData).position(0);
    }

    public String toString(){
        return "Center (x,y)=("+ _xCenter +";"+ _yCenter +"); MidWidth:"+ _midWidth +", MidHeight:"+ _midHeight;
    }

    /**
     * Draw the gl object on the GLSurfaceView
     */
    public void draw(){
        /** texture **/
        //Log.i(TAG, "Drawing");
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);

        _squareVertice.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _squareVertice);
        checkGlError("glVertexAttribPointer maPosition", maPositionHandle);

        _squareVertice.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        checkGlError("glEnableVertexAttribArray maPositionHandle", maPositionHandle);
        GLES20.glVertexAttribPointer(maTextureHandle,  2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _squareVertice);
        checkGlError("glVertexAttribPointer maTextureHandle", maTextureHandle);
        GLES20.glEnableVertexAttribArray(maTextureHandle);
        checkGlError("glEnableVertexAttribArray maTextureHandle", maTextureHandle);

        Matrix.rotateM(rotation, 0, _angle, 0.0f,0.0f,1.0f);
        // rotation
        //Matrix.multiplyMM(mVMatrix, 0, rotation, 0, mVMatrix, 0);

        Matrix.setRotateM(mMMatrix, 0, 90.0f, 0, 0, 1.0f);
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);

        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        checkGlError("glDrawArrays", muMVPMatrixHandle);

        _squareVertice.position(TRIANGLE_VERTICES_DATA_POS_OFFSET + 5);
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _squareVertice);
        checkGlError("glVertexAttribPointer maPosition", maPositionHandle);
        _squareVertice.position(TRIANGLE_VERTICES_DATA_UV_OFFSET + 5);
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        checkGlError("glEnableVertexAttribArray maPositionHandle", maPositionHandle);

        GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _squareVertice);
        checkGlError("glVertexAttribPointer maTextureHandle", maTextureHandle);
        GLES20.glEnableVertexAttribArray(maTextureHandle);
        checkGlError("glEnableVertexAttribArray maTextureHandle", maTextureHandle);

        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);


        Matrix.setRotateM(mMMatrix, 0, 90.0f, 0, 0, 1.0f);

        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        checkGlError("glDrawArrays", muMVPMatrixHandle);
    }
}
